/*
 * Copyright (C) 2025 TU Dresden
 * Copyright (C) 2021 HAW Hamburg
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

#pragma once

/**
 * @ingroup     sys_psa_crypto
 * @{
 *
 * @file
 * @brief       Cipher size definitions for the PSA Crypto API
 *
 * @author      Armin Wolf <wolf.armin@mailbox.tu-dresden.de>
 * @author      Lena Boeckmann <lena.boeckmann@haw-hamburg.de>
 *
 */

#ifdef __cplusplus
extern "C" {
#endif

#include "kernel_defines.h"
#include "psa/cipher/algorithm.h"
#include "psa/key/type.h"

/**
 * @brief   The block size of a block cipher.
 *
 * @note    It is possible to build stream cipher algorithms on top of a block cipher,
 *          for example CTR mode (@ref PSA_ALG_CTR). This macro only takes the key type
 *          into account, so it cannot be used to determine the size of the data that
 *          @ref psa_cipher_update() might buffer for future processing in general.
 *
 * @param   type  A cipher key type (value of type @ref psa_key_type_t).
 *
 * @return  The block size for a block cipher, or 1 for a stream cipher.
 */
#define PSA_BLOCK_CIPHER_BLOCK_LENGTH(type)                                     \
    (1u << (((type) >> 8) & 7))

/**
 * @brief   The maximum block size of a block cipher supported by the implementation.
 *
 * @details See also @ref PSA_BLOCK_CIPHER_BLOCK_LENGTH().
 */
#define PSA_BLOCK_CIPHER_BLOCK_MAX_SIZE /* implementation-defined value */

/**
 * @brief   The default IV size for a cipher algorithm, in bytes.
 *
 * @details The IV that is generated as part of a call to @ref psa_cipher_encrypt() is always
 *          the default IV length for the algorithm.
 *
 *          This macro can be used to allocate a buffer of sufficient size to
 *          store the IV output from @ref psa_cipher_generate_iv() when using
 *          a multi-part cipher operation.
 *
 *          See also @ref PSA_CIPHER_IV_MAX_SIZE.
 *
 * @warning This macro may evaluate its arguments multiple times or
 *          zero times, so you should not pass arguments that contain
 *          side effects.
 *
 * @param   key_type    A symmetric key type that is compatible with algorithm alg.
 *
 * @param   alg         A cipher algorithm (PSA_ALG_XXX value such that
 *                      @ref PSA_ALG_IS_CIPHER(@p alg) is true)
 *
 * @return  The default IV size for the specified key type and algorithm.
 *          0, if the algorithm does not use an IV, if key type or cipher
 *          algorithm are not recognized or if the parameters are not compatible.
 *
 */
#define PSA_CIPHER_IV_LENGTH(key_type, alg) \
    ((PSA_BLOCK_CIPHER_BLOCK_LENGTH(key_type) > 1 && \
     ((alg) == PSA_ALG_CBC_NO_PADDING)) ? 16 : \
     (key_type == PSA_KEY_TYPE_CHACHA20) ? 12 : 0)

/**
 * @brief   A sufficient buffer size for storing the IV generated by @ref psa_cipher_generate_iv(),
 *          for any of the supported key types and cipher algorithms.
 *
 * @details If the size of the IV buffer is at least this large, it is guaranteed that
 *          @ref psa_cipher_generate_iv() will not fail due to an insufficient buffer size.
 *
 *          See also @ref PSA_CIPHER_IV_LENGTH().
 */
#define PSA_CIPHER_IV_MAX_SIZE /* implementation-defined value */

/**
 * @brief   The maximum size of the output of @ref psa_cipher_encrypt(), in bytes.
 *
 * @details If the size of the output buffer is at least this large, it is guaranteed
 *          that @ref psa_cipher_encrypt() will not fail due to an insufficient buffer size.
 *          Depending on the algorithm, the actual size of the output might be smaller.
 *
 *          See also @ref PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE.
 *
 * @param   key_type        A symmetric key type that is compatible with algorithm alg.
 * @param   alg             A cipher algorithm (PSA_ALG_XXX value such that
 *                          @ref PSA_ALG_IS_CIPHER(@p alg) is true).
 * @param   input_length    Size of the input in bytes.
 *
 * @return  A sufficient output size for the specified key type and algorithm.
 *          0 if the key type or cipher algorithm is not recognized, not supported or the
 *          parameters are incompatible.
 */
#define PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(key_type, alg, input_length) \
    (input_length + PSA_CIPHER_IV_LENGTH(key_type, alg))

/**
 * @brief   A sufficient output buffer size for @ref psa_cipher_encrypt(), for any of the supported
 *          key types and cipher algorithms.
 *
 * @details If the size of the output buffer is at least this large, it is guaranteed that
 *          @ref psa_cipher_encrypt() will not fail due to an insufficient buffer size.
 *
 *          See also @ref PSA_CIPHER_ENCRYPT_OUTPUT_SIZE().
 *
 * @param   input_length Size of the input in bytes.
 */
#define PSA_CIPHER_ENCRYPT_OUTPUT_MAX_SIZE(input_length) \
    (PSA_CIPHER_ENCRYPT_OUTPUT_SIZE(PSA_KEY_TYPE_AES, PSA_ALG_CBC_NO_PADDING, input_length))

/**
 * @brief   The maximum size of the output of @ref psa_cipher_decrypt(), in bytes.
 *
 * @details If the size of the output buffer is at least this large, it is guaranteed
 *          that @ref psa_cipher_decrypt() will not fail due to an insufficient buffer size.
 *          Depending on the algorithm, the actual size of the output might be smaller.
 *
 *          See also @ref PSA_CIPHER_DECRYPT_OUTPUT_MAX_SIZE.
 *
 * @param   key_type        A symmetric key type that is compatible with algorithm alg.
 * @param   alg             A cipher algorithm (PSA_ALG_XXX value such that
 *                          @ref PSA_ALG_IS_CIPHER(@p alg) is true).
 * @param   input_length    Size of the input in bytes.
 *
 * @return  A sufficient output size for the specified key type and algorithm.
 *          0 if the key type or cipher algorithm is not recognized, or the parameters
 *          are incompatible.
 */
#define PSA_CIPHER_DECRYPT_OUTPUT_SIZE(key_type, alg, input_length) \
    (input_length - PSA_CIPHER_IV_LENGTH(key_type, alg))

/**
 * @brief   A sufficient output buffer size for @ref psa_cipher_decrypt(), for any of the supported
 *          key types and cipher algorithms.
 *
 * @details If the size of the output buffer is at least this large, it is guaranteed that
 *          @ref psa_cipher_decrypt() will not fail due to an insufficient buffer size.
 *
 *          See also @ref PSA_CIPHER_DECRYPT_OUTPUT_SIZE().
 *
 * @param   input_length Size of the input in bytes.
 */
#define PSA_CIPHER_DECRYPT_OUTPUT_MAX_SIZE(input_length) \
    (input_length)

/**
 * @brief   A sufficient output buffer size for @ref psa_cipher_update().
 *
 * @details If the size of the output buffer is at least this large,
 *          it is guaranteed that @ref psa_cipher_update() will not fail
 *          due to an insufficient buffer size. The actual size of the
 *          output might be smaller in any given call.
 *
 *          See also @ref PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE.
 *
 * @param   key_type        A symmetric key type that is compatible with algorithm alg.
 * @param   alg             A cipher algorithm (PSA_ALG_XXX value such that
 *                          @ref PSA_ALG_IS_CIPHER(alg) is true).
 * @param   input_length    Size of the input in bytes.
 *
 * @return  A sufficient output size for the specified key type and algorithm.
 *          0 if the key type or cipher algorithm is not recognized, not supported or the parameters
 *          are incompatible.
 */
#define PSA_CIPHER_UPDATE_OUTPUT_SIZE(key_type, alg, input_length) \
/* implementation-defined value */

/**
 * @brief   A sufficient output buffer size for @ref psa_cipher_update(),
 *          for any of the supported key types and cipher algorithms.
 *
 * @details If the size of the output buffer is at least this large,
 *          it is guaranteed that @ref psa_cipher_update() will not fail
 *          due to an insufficient buffer size.
 *
 *          See also @ref PSA_CIPHER_UPDATE_OUTPUT_SIZE().
 *
 * @param input_length  Size of the input in bytes.
 */
#define PSA_CIPHER_UPDATE_OUTPUT_MAX_SIZE(input_length) \
/* implementation-defined value */

/**
 * @brief   A sufficient output buffer size for @ref psa_cipher_finish().
 *
 * @details If the size of the output buffer is at least this large, it is guaranteed that
 *          @ref psa_cipher_finish() will not fail due to an insufficient buffer size.
 *          The actual size of the output might be smaller in any given call.
 *
 *          See also @ref PSA_CIPHER_FINISH_OUTPUT_MAX_SIZE.
 *
 * @param   key_type    A symmetric key type that is compatible with algorithm alg.
 * @param   alg         A cipher algorithm: a value of type psa_algorithm_t such that
 *                      @ref PSA_ALG_IS_CIPHER(@p alg) is true.
 *
 * @return  A sufficient output size for the specified key type and algorithm.
 *          0 if the key type or cipher algorithm is not recognized, not supported or the
 *          parameters are incompatible.
 */
#define PSA_CIPHER_FINISH_OUTPUT_SIZE(key_type, alg) \
/* implementation-defined value */

/**
 * @brief   A sufficient output buffer size for @ref psa_cipher_finish(), for any of the supported
 *          key types and cipher algorithms.
 *
 * @details If the size of the output buffer is at least this large, it is guaranteed that
 *          @ref psa_cipher_finish() will not fail due to an insufficient buffer size.
 *
 *          See also @ref PSA_CIPHER_FINISH_OUTPUT_SIZE().
 */
#define PSA_CIPHER_FINISH_OUTPUT_MAX_SIZE /* implementation-defined value */

#ifdef __cplusplus
}
#endif

/** @} */
